---
title: Typed routes
description: Learn how to use statically typed links and routes in Expo Router.
---

import { FileTree } from '~/ui/components/FileTree';

> Available when using TypeScript in your project. Expo Router supports standard TypeScript out of the box. See the [TypeScript](/guides/typescript/) guide for more information on how to set it up.

Expo Router supports generating TypeScript types automatically with Expo CLI. This enables `<Link>`, and the [hooks API](/router/reference/hooks) to be statically typed. This feature is currently in beta and is not enabled by default.

## Get started

### Quick start

If you created your project using the [Expo Router quick start guide](/router/installation/#quick-start) then your project is already configured to use typed routes. The Expo CLI will generate the required type file the first time you run `npx expo start`. Then, you can use autocomplete for `href` props whenever you use an Expo Router `<Link>` component in a **.tsx** file.

### Manual configuration

While the feature is in beta, you can enable it by setting `experiments.typedRoutes` to `true` in your **app.json**:

```json app.json
{
  "expo": {
    "experiments": {
      "typedRoutes": true
    }
  }
}
```

Run `npx expo customize tsconfig.json` to configure your **tsconfig.json** to add the required `includes` fields.

Then, start the development server by running `npx expo start`. You can now use autocomplete in the Expo Router `<Link>` component's `href` prop.

## Type generation

Typed routes in Expo Router are automatically generated when the development server starts. By default, these generated types are configured to be untracked by Git and will be added to the local **.gitignore** file. This ensures that autogenerated files do not clutter your version control system.

If you find yourself in a situation where you need to generate these types without initiating the development server, such as during type checking on a Continuous Integration (CI) server. To do this, run the command `npx expo customize tsconfig.json` on the CI.

## Statically typed routes

Components and functions that use `Href<T>` will now be statically typed and have a much stricter definition. For example:

```tsx
✅ <Link href="/about" />
✅ <Link href="/user/1" />
✅ <Link href={`/user/${id}`} />
✅ <Link href={("/user" + id) as Href} />
// TypeScript errors if href is not a valid route
❌ <Link href="/usser/1" />
```

For dynamic routes, Href's need to be objects and their parameters are strictly typed:

```tsx
✅ <Link href={{ pathname: "/user/[id]", params: { id: 1 }}} />
// TypeScript errors as href is valid, but it should be a HrefObject with params
❌ <Link href="/user/[id]" />
// TypeScript errors as params contain invalid keys
❌ <Link href={{ pathname: "/user/[id]", params: { _id: 1 }}} />
// TypeScript errors as params contain unknown keys
❌ <Link href={{ pathname: "/user/[id]", params: { id: 1, id2: 2 }}} />
```

### Relative paths

Statically typed routes do not support relative paths. You'll need to use absolute paths for all routes:

```tsx
✅ <Link href="/about" />

// Relative paths are not supported
❌ <Link href="./about" />
```

You can leverage the `useSegments()` hooks from `expo-router` to create complex relative paths. Consider the following structure:

<FileTree
  files={[
    'app/(feed)/_layout.tsx',
    'app/(feed)/feed.tsx',
    'app/(feed)/search.tsx',
    'app/(feed)/profile.tsx',
    'app/(search)/profile.tsx',
    'components/button.tsx',
  ]}
/>

You can ensure that you push to the same tab by using the `useSegments()` hook to get the first segment of the current route.

```tsx button.tsx
import { Link, useSegments } from 'expo-router';

export function Button() {
  const [
    // This will be either `(feed)` or `(search)` depending on the current tab.
    first,
  ] = useSegments();

  return <Link href={`/${first}/profile`}>Push profile</Link>;
}
```

Now, you can leverage `<Button />` from both **app/(feed)/feed.tsx** and **app/(search)/search.tsx** to push `./profile` while preserving the current tab.

## Imperative navigation

You can use the typed `router` object to navigate imperatively:

```tsx
import { router } from 'expo-router';

router.push('/about');
```

Or with the typed `useRouter()` hook:

```tsx
import { useRouter } from 'expo-router';

function Page() {
  const router = useRouter();

  router.push('/about');

  // ...
}
```

## Query parameters

Most query parameters will not be represented in the file system and therefore cannot be typed automatically. You can type query parameters manually by passing a generic to the `useLocalSearchParams` and `useGlobalSearchParams` hooks. For example:

```tsx app/search.tsx
import { Text } from 'react-native';
import { useLocalSearchParams } from 'expo-router';

export default function Page() {
  /* @info Manually typed additional query parameters */
  const { query } = useLocalSearchParams<{ query?: string }>();
  /* @end */

  return <Text>Search: {query ?? 'unset'}</Text>;
}
```

## Changes made to the environment

When typed routes is enabled, Expo CLI will generate a git ignored **expo-env.d.ts** file in your project's root directory, update the **.gitignore** to ignore the new root **expo-env.d.ts** file, and modify the **tsconfig.json** to include the new **expo-env.d.ts** file.

The `includes` field in your **tsconfig.json** gets updated to include **expo-env.d.ts** and a hidden **.expo** directory. These entries are required and should not be removed from the file.

The generated **expo-env.d.ts** should not be removed or changed at any time. It should not be committed and should be ignored by version control.

### Global types

Expo CLI will add the following global types to your project when typed routes is enabled:

- Sets `process.env.NODE_ENV = "development" | "production" | "test"`
- Allows the importing of `.[css|sass|scss]` files
- Sets the exports of `*.module.[css|sass|scss]` to be `Record<string, string>`
- Add types for Metro's `require.context`. This is enabled by `expo/metro-config` and used for static route generation.

### React Native Web

With typed routes enabled, Expo CLI also augments the `react-native` types to support React Native Web. The following changes are made:

- Add additional web-only styles for `ViewStyle`, `TextStyle`, `ImageStyle`
- Add `tabIndex`, `aria-level`, `lang` to `TextProps`
- Add `hovered` to Pressable's `children` and `style` state callback function
- Add `className` elements
